package com.yanyeori.framework.datasource.mybatisplus.injector;

import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.metadata.TableFieldInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.yanyeori.framework.datasource.mybatisplus.annotation.MultiTableId;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlSource;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * 根据联合主键修改数据，全量更新，不忽略null字段
 *
 * @author chenkuan 2021/5/28
 */
public class UpdateAllByMultiIdMethod extends AbstractMethod {

    @Override
    public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
        final String sql = "<script>update %s set %s where %s</script>";
        Field[] fields = getEntityFields(tableInfo.getEntityType());
        final String setSql = prepareSql(tableInfo, fields, false, ",");
        final String whereSql = prepareSql(tableInfo, fields, true, " and ");
        final String sqlResult = String.format(sql, tableInfo.getTableName(), setSql, whereSql);
        SqlSource sqlSource = languageDriver.createSqlSource(configuration, sqlResult, modelClass);
        return addUpdateMappedStatement(mapperClass, modelClass, getMethod(), sqlSource);
    }

    private Field[] getEntityFields(Class<?> entityType) {
        return entityType.getDeclaredFields();
    }

    /**
     * 拼接sql (field1=value1,field2=value2,...)
     */
    private String prepareSql(TableInfo tableInfo, Field[] fields, boolean isMultiId, String sep) {
        StringBuilder builderSql = new StringBuilder();
        Set<String> propertySet = new HashSet<>();
        for (Field field : fields) {
            if (isMultiId && field.isAnnotationPresent(MultiTableId.class)) {
                propertySet.add(field.getName());
            } else if (!isMultiId && !field.isAnnotationPresent(MultiTableId.class)) {
                propertySet.add(field.getName());
            }
        }
        Map<String, String> map = new HashMap<>();
        if (tableInfo.getKeyColumn() != null && propertySet.contains(tableInfo.getKeyProperty())) {
            map.put(tableInfo.getKeyColumn(), tableInfo.getKeyProperty());
        }
        for (TableFieldInfo fieldInfo : tableInfo.getFieldList()) {
            if (propertySet.contains(fieldInfo.getProperty())) {
                map.put(fieldInfo.getColumn(), fieldInfo.getProperty());
            }
        }
        map.forEach((column, property) -> {
            builderSql.append(column).append("=").append("#{").append(Constants.ENTITY).append(".").append(property).append("}");
            builderSql.append(sep);
        });
        if (builderSql.length() > 0) {
            builderSql.delete(builderSql.length() - sep.length(), builderSql.length());
        }
        return builderSql.toString();
    }

    private String getMethod() {
        return "updateAllByMultiId";
    }
}
